#include "eventloop.h"
#include "event.h"
#include "tinyrpc/base/currentThread.h"
#include "tinyrpc/base/task.h"
#include "coroutine_sync.h"

#include <iostream>
namespace TinyRPC{

thread_local EventLoop* currentEventLoop = 0;

EventLoop::EventLoop()
: m_epoller(std::unique_ptr<EPoller>(new EPoller()))
, m_quit(false)
, m_ownerThreadTid(CurrentThread::Tid())
{
    if(currentEventLoop != 0){
        exit(0);
    }else{
        currentEventLoop = this;
    }

    // 唤醒channel 与 定时器均将自己注册到EventLoop里面
    m_wakeupChannel.wakeupUpdateEvent(currentEventLoop);
    m_timerQueue.timerQueueUpdateEvent(currentEventLoop);
    // eventloop 每一秒唤醒自己
    runEvery(1, 1, Task([&](){
        this->wakeup();
    }, false));
}

void EventLoop::quit(){
    m_quit = true;
    //if(m_status == EventLoopStatus::STATUS_EPOLLWAIT){
    wakeup();
}

void EventLoop::loop(){
    while (!m_quit)
    {
        m_activateEvents.clear();
        m_status = EventLoopStatus::STATUS_EPOLLWAIT;
        m_epoller->poll(m_activateEvents);

        m_status = EventLoopStatus::STATUS_HANDLEEVENT;
        for(Event* event : m_activateEvents){
            event->handleEvent();  
        }

        m_status = EventLoopStatus::STATUS_DOTASK;
        doTask();
    }
}

    
EventLoop* EventLoop::CurrentEventLoop(){
    return currentEventLoop;
}


void EventLoop::doTask(){
    std::vector<Task> taskList;
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        taskList.swap(m_taskList);
    }

    for(Task& task : taskList){
        task.run();
    }

}

void EventLoop::queueInLoop(const Task& task){
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_taskList.push_back(task);
    }

    // if(m_status == EventLoopStatus::STATUS_EPOLLWAIT || 
    //             m_status == EventLoopStatus::STATUS_DOTASK)
    wakeup();
}
void EventLoop::runInLoop(const Task& task){
    // 当由协程来调用另一个协程时, 只能queueInLoop 保证由主线程来调用
    if(Coroutine::InCoroutine() && task.isCoroutine()){
        queueInLoop(task);
    }
    else if(CurrentThread::Tid() == m_ownerThreadTid){
        task.run();
    }else{
        queueInLoop(task);
    }
}

void EventLoop::setOwnerThread(pid_t tid){
    m_ownerThreadTid = tid;
}


void EventLoop::addEvent(EventPtr event){
    m_epoller->addEvent(event);
}

void EventLoop::modifyEvent(EventPtr event){
    m_epoller->modifyEvent(event);
}

void EventLoop::deleteEvent(EventPtr event){
    m_epoller->deleteEvent(event->getFd());
}

void EventLoop::wakeup(){
    m_wakeupChannel.write();
}

void EventLoop::runAfter(float sec, const Task& task){
    Timer timer(TimeStamp::Now() + sec*TimeStamp::kMicroSecPerSecond, 0, task );
    m_timerQueue.addTimer(timer);
}

void EventLoop::runEvery(float sec, float interval, const Task& task){
    Timer timer(TimeStamp::Now() + TimeStamp::kMicroSecPerSecond*sec, interval * TimeStamp::kMicroSecPerSecond, task);
    m_timerQueue.addTimer(timer);
}
}

